1   /*
2    * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.applet;
27  
28  import java.util.*;
29  import java.io.*;
30  import java.awt.*;
31  import java.awt.event.*;
32  import java.awt.print.*;
33  import javax.print.attribute.*;
34  import java.applet.*;
35  import java.net.URL;
36  import java.net.MalformedURLException;
37  import java.net.SocketPermission;
38  import sun.misc.Ref;
39  import java.security.AccessController;
40  import java.security.PrivilegedAction;
41  import java.lang.reflect.InvocationTargetException;
42  import java.lang.reflect.Method;
43  import sun.awt.SunToolkit;
44  import sun.awt.AppContext;
45  import java.lang.ref.WeakReference;
46  
47  /**
48   * A frame to show the applet tag in.
49   */
50  class TextFrame extends Frame {
51  
52      /**
53       * Create the tag frame.
54       */
55      TextFrame(int x, int y, String title, String text) {
56          setTitle(title);
57          TextArea txt = new TextArea(20, 60);
58          txt.setText(text);
59          txt.setEditable(false);
60  
61          add("Center", txt);
62  
63          Panel p = new Panel();
64          add("South", p);
65          Button b = new Button(amh.getMessage("button.dismiss", "Dismiss"));
66          p.add(b);
67  
68          class ActionEventListener implements ActionListener {
69              public void actionPerformed(ActionEvent evt) {
70                  dispose();
71              }
72          }
73          b.addActionListener(new ActionEventListener());
74  
75          pack();
76          move(x, y);
77          setVisible(true);
78  
79          WindowListener windowEventListener = new WindowAdapter() {
80  
81              public void windowClosing(WindowEvent evt) {
82                  dispose();
83              }
84          };
85  
86          addWindowListener(windowEventListener);
87      }
88      private static AppletMessageHandler amh = new AppletMessageHandler("textframe");
89  
90  }
91  
92  /**
93   * Lets us construct one using unix-style one shot behaviors
94   */
95  
96  class StdAppletViewerFactory implements AppletViewerFactory
97  {
98      public AppletViewer createAppletViewer(int x, int y,
99                                             URL doc, Hashtable atts) {
100         return new AppletViewer(x, y, doc, atts, System.out, this);
101     }
102 
103     public MenuBar getBaseMenuBar() {
104         return new MenuBar();
105     }
106 
107     public boolean isStandalone() {
108         return true;
109     }
110 }
111 
112 /**
113  * The applet viewer makes it possible to run a Java applet without using a browser.
114  * For details on the syntax that <B>appletviewer</B> supports, see
115  * <a href="../../../docs/tooldocs/appletviewertags.html">AppletViewer Tags</a>.
116  * (The document named appletviewertags.html in the JDK's docs/tooldocs directory,
117  *  once the JDK docs have been installed.)
118  */
119 public class AppletViewer extends Frame implements AppletContext,
120                                                    Printable {
121 
122     /**
123      * Some constants...
124      */
125     private static String defaultSaveFile = "Applet.ser";
126 
127     /**
128      * The panel in which the applet is being displayed.
129      */
130     AppletViewerPanel panel;
131 
132     /**
133      * The status line.
134      */
135     Label label;
136 
137     /**
138      * output status messages to this stream
139      */
140 
141     PrintStream statusMsgStream;
142 
143     /**
144      * For cloning
145      */
146     AppletViewerFactory factory;
147 
148 
149     private final class UserActionListener implements ActionListener {
150         public void actionPerformed(ActionEvent evt) {
151             processUserAction(evt);
152         }
153     }
154 
155     /**
156      * Create the applet viewer
157      */
158     public AppletViewer(int x, int y, URL doc, Hashtable atts,
159                         PrintStream statusMsgStream, AppletViewerFactory factory) {
160         this.factory = factory;
161         this.statusMsgStream = statusMsgStream;
162         setTitle(amh.getMessage("tool.title", atts.get("code")));
163 
164         MenuBar mb = factory.getBaseMenuBar();
165 
166         Menu m = new Menu(amh.getMessage("menu.applet"));
167 
168         addMenuItem(m, "menuitem.restart");
169         addMenuItem(m, "menuitem.reload");
170         addMenuItem(m, "menuitem.stop");
171         addMenuItem(m, "menuitem.save");
172         addMenuItem(m, "menuitem.start");
173         addMenuItem(m, "menuitem.clone");
174         m.add(new MenuItem("-"));
175         addMenuItem(m, "menuitem.tag");
176         addMenuItem(m, "menuitem.info");
177         addMenuItem(m, "menuitem.edit").disable();
178         addMenuItem(m, "menuitem.encoding");
179         m.add(new MenuItem("-"));
180         addMenuItem(m, "menuitem.print");
181         m.add(new MenuItem("-"));
182         addMenuItem(m, "menuitem.props");
183         m.add(new MenuItem("-"));
184         addMenuItem(m, "menuitem.close");
185         if (factory.isStandalone()) {
186             addMenuItem(m, "menuitem.quit");
187         }
188 
189         mb.add(m);
190 
191         setMenuBar(mb);
192 
193         add("Center", panel = new AppletViewerPanel(doc, atts));
194         add("South", label = new Label(amh.getMessage("label.hello")));
195         panel.init();
196         appletPanels.addElement(panel);
197 
198         pack();
199         move(x, y);
200         setVisible(true);
201 
202         WindowListener windowEventListener = new WindowAdapter() {
203 
204             public void windowClosing(WindowEvent evt) {
205                 appletClose();
206             }
207 
208             public void windowIconified(WindowEvent evt) {
209                 appletStop();
210             }
211 
212             public void windowDeiconified(WindowEvent evt) {
213                 appletStart();
214             }
215         };
216 
217         class AppletEventListener implements AppletListener
218         {
219             final Frame frame;
220 
221             public AppletEventListener(Frame frame)
222             {
223                 this.frame = frame;
224             }
225 
226             public void appletStateChanged(AppletEvent evt)
227             {
228                 AppletPanel src = (AppletPanel)evt.getSource();
229 
230                 switch (evt.getID()) {
231                     case AppletPanel.APPLET_RESIZE: {
232                         if(src != null) {
233                             resize(preferredSize());
234                             validate();
235                         }
236                         break;
237                     }
238                     case AppletPanel.APPLET_LOADING_COMPLETED: {
239                         Applet a = src.getApplet(); // sun.applet.AppletPanel
240 
241                         // Fixed #4754451: Applet can have methods running on main
242                         // thread event queue.
243                         //
244                         // The cause of this bug is that the frame of the applet
245                         // is created in main thread group. Thus, when certain
246                         // AWT/Swing events are generated, the events will be
247                         // dispatched through the wrong event dispatch thread.
248                         //
249                         // To fix this, we rearrange the AppContext with the frame,
250                         // so the proper event queue will be looked up.
251                         //
252                         // Swing also maintains a Frame list for the AppContext,
253                         // so we will have to rearrange it as well.
254                         //
255                         if (a != null)
256                             AppletPanel.changeFrameAppContext(frame, SunToolkit.targetToAppContext(a));
257                         else
258                             AppletPanel.changeFrameAppContext(frame, AppContext.getAppContext());
259 
260                         break;
261                     }
262                 }
263             }
264         };
265 
266         addWindowListener(windowEventListener);
267         panel.addAppletListener(new AppletEventListener(this));
268 
269         // Start the applet
270         showStatus(amh.getMessage("status.start"));
271         initEventQueue();
272     }
273 
274     // XXX 99/9/10 probably should be "private"
275     public MenuItem addMenuItem(Menu m, String s) {
276         MenuItem mItem = new MenuItem(amh.getMessage(s));
277         mItem.addActionListener(new UserActionListener());
278         return m.add(mItem);
279     }
280 
281     /**
282      * Send the initial set of events to the appletviewer event queue.
283      * On start-up the current behaviour is to load the applet and call
284      * Applet.init() and Applet.start().
285      */
286     private void initEventQueue() {
287         // appletviewer.send.event is an undocumented and unsupported system
288         // property which is used exclusively for testing purposes.
289         String eventList = System.getProperty("appletviewer.send.event");
290 
291         if (eventList == null) {
292             // Add the standard events onto the event queue.
293             panel.sendEvent(AppletPanel.APPLET_LOAD);
294             panel.sendEvent(AppletPanel.APPLET_INIT);
295             panel.sendEvent(AppletPanel.APPLET_START);
296         } else {
297             // We're testing AppletViewer.  Force the specified set of events
298             // onto the event queue, wait for the events to be processed, and
299             // exit.
300 
301             // The list of events that will be executed is provided as a
302             // ","-separated list.  No error-checking will be done on the list.
303             String [] events = splitSeparator(",", eventList);
304 
305             for (int i = 0; i < events.length; i++) {
306                 System.out.println("Adding event to queue: " + events[i]);
307                 if (events[i].equals("dispose"))
308                     panel.sendEvent(AppletPanel.APPLET_DISPOSE);
309                 else if (events[i].equals("load"))
310                     panel.sendEvent(AppletPanel.APPLET_LOAD);
311                 else if (events[i].equals("init"))
312                     panel.sendEvent(AppletPanel.APPLET_INIT);
313                 else if (events[i].equals("start"))
314                     panel.sendEvent(AppletPanel.APPLET_START);
315                 else if (events[i].equals("stop"))
316                     panel.sendEvent(AppletPanel.APPLET_STOP);
317                 else if (events[i].equals("destroy"))
318                     panel.sendEvent(AppletPanel.APPLET_DESTROY);
319                 else if (events[i].equals("quit"))
320                     panel.sendEvent(AppletPanel.APPLET_QUIT);
321                 else if (events[i].equals("error"))
322                     panel.sendEvent(AppletPanel.APPLET_ERROR);
323                 else
324                     // non-fatal error if we get an unrecognized event
325                     System.out.println("Unrecognized event name: " + events[i]);
326             }
327 
328             while (!panel.emptyEventQueue()) ;
329             appletSystemExit();
330         }
331     }
332 
333     /**
334      * Split a string based on the presence of a specified separator.  Returns
335      * an array of arbitrary length.  The end of each element in the array is
336      * indicated by the separator of the end of the string.  If there is a
337      * separator immediately before the end of the string, the final element
338      * will be empty.  None of the strings will contain the separator.  Useful
339      * when separating strings such as "foo/bar/bas" using separator "/".
340      *
341      * @param sep  The separator.
342      * @param s    The string to split.
343      * @return     An array of strings.  Each string in the array is determined
344      *             by the location of the provided sep in the original string,
345      *             s.  Whitespace not stripped.
346      */
347     private String [] splitSeparator(String sep, String s) {
348         Vector v = new Vector();
349         int tokenStart = 0;
350         int tokenEnd   = 0;
351 
352         while ((tokenEnd = s.indexOf(sep, tokenStart)) != -1) {
353             v.addElement(s.substring(tokenStart, tokenEnd));
354             tokenStart = tokenEnd+1;
355         }
356         // Add the final element.
357         v.addElement(s.substring(tokenStart));
358 
359         String [] retVal = new String[v.size()];
360         v.copyInto(retVal);
361         return retVal;
362     }
363 
364     /*
365      * Methods for java.applet.AppletContext
366      */
367 
368     private static Map audioClips = new HashMap();
369 
370     /**
371      * Get an audio clip.
372      */
373     public AudioClip getAudioClip(URL url) {
374         checkConnect(url);
375         synchronized (audioClips) {
376             AudioClip clip = (AudioClip)audioClips.get(url);
377             if (clip == null) {
378                 audioClips.put(url, clip = new AppletAudioClip(url));
379             }
380             return clip;
381         }
382     }
383 
384     private static Map imageRefs = new HashMap();
385 
386     /**
387      * Get an image.
388      */
389     public Image getImage(URL url) {
390         return getCachedImage(url);
391     }
392 
393     static Image getCachedImage(URL url) {
394         // System.getSecurityManager().checkConnection(url.getHost(), url.getPort());
395         return (Image)getCachedImageRef(url).get();
396     }
397 
398     /**
399      * Get an image ref.
400      */
401     static Ref getCachedImageRef(URL url) {
402         synchronized (imageRefs) {
403             AppletImageRef ref = (AppletImageRef)imageRefs.get(url);
404             if (ref == null) {
405                 ref = new AppletImageRef(url);
406                 imageRefs.put(url, ref);
407             }
408             return ref;
409         }
410     }
411 
412     /**
413      * Flush the image cache.
414      */
415     static void flushImageCache() {
416         imageRefs.clear();
417     }
418 
419     static Vector appletPanels = new Vector();
420 
421     /**
422      * Get an applet by name.
423      */
424     public Applet getApplet(String name) {
425         AppletSecurity security = (AppletSecurity)System.getSecurityManager();
426         name = name.toLowerCase();
427         SocketPermission panelSp =
428             new SocketPermission(panel.getCodeBase().getHost(), "connect");
429         for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) {
430             AppletPanel p = (AppletPanel)e.nextElement();
431             String param = p.getParameter("name");
432             if (param != null) {
433                 param = param.toLowerCase();
434             }
435             if (name.equals(param) &&
436                 p.getDocumentBase().equals(panel.getDocumentBase())) {
437 
438                 SocketPermission sp =
439                     new SocketPermission(p.getCodeBase().getHost(), "connect");
440 
441                 if (panelSp.implies(sp)) {
442                     return p.applet;
443                 }
444             }
445         }
446         return null;
447     }
448 
449     /**
450      * Return an enumeration of all the accessible
451      * applets on this page.
452      */
453     public Enumeration getApplets() {
454         AppletSecurity security = (AppletSecurity)System.getSecurityManager();
455         Vector v = new Vector();
456         SocketPermission panelSp =
457             new SocketPermission(panel.getCodeBase().getHost(), "connect");
458 
459         for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) {
460             AppletPanel p = (AppletPanel)e.nextElement();
461             if (p.getDocumentBase().equals(panel.getDocumentBase())) {
462 
463                 SocketPermission sp =
464                     new SocketPermission(p.getCodeBase().getHost(), "connect");
465                 if (panelSp.implies(sp)) {
466                     v.addElement(p.applet);
467                 }
468             }
469         }
470         return v.elements();
471     }
472 
473     /**
474      * Ignore.
475      */
476     public void showDocument(URL url) {
477     }
478 
479     /**
480      * Ignore.
481      */
482     public void showDocument(URL url, String target) {
483     }
484 
485     /**
486      * Show status.
487      */
488     public void showStatus(String status) {
489         label.setText(status);
490     }
491 
492     public void setStream(String key, InputStream stream)throws IOException{
493         // We do nothing.
494     }
495 
496     public InputStream getStream(String key){
497         // We do nothing.
498         return null;
499     }
500 
501     public Iterator getStreamKeys(){
502         // We do nothing.
503         return null;
504     }
505 
506     /**
507      * System parameters.
508      */
509     static Hashtable systemParam = new Hashtable();
510 
511     static {
512         systemParam.put("codebase", "codebase");
513         systemParam.put("code", "code");
514         systemParam.put("alt", "alt");
515         systemParam.put("width", "width");
516         systemParam.put("height", "height");
517         systemParam.put("align", "align");
518         systemParam.put("vspace", "vspace");
519         systemParam.put("hspace", "hspace");
520     }
521 
522     /**
523      * Print the HTML tag.
524      */
525     public static void printTag(PrintStream out, Hashtable atts) {
526         out.print("<applet");
527 
528         String v = (String)atts.get("codebase");
529         if (v != null) {
530             out.print(" codebase=\"" + v + "\"");
531         }
532 
533         v = (String)atts.get("code");
534         if (v == null) {
535             v = "applet.class";
536         }
537         out.print(" code=\"" + v + "\"");
538         v = (String)atts.get("width");
539         if (v == null) {
540             v = "150";
541         }
542         out.print(" width=" + v);
543 
544         v = (String)atts.get("height");
545         if (v == null) {
546             v = "100";
547         }
548         out.print(" height=" + v);
549 
550         v = (String)atts.get("name");
551         if (v != null) {
552             out.print(" name=\"" + v + "\"");
553         }
554         out.println(">");
555 
556         // A very slow sorting algorithm
557         int len = atts.size();
558         String params[] = new String[len];
559         len = 0;
560         for (Enumeration e = atts.keys() ; e.hasMoreElements() ;) {
561             String param = (String)e.nextElement();
562             int i = 0;
563             for (; i < len ; i++) {
564                 if (params[i].compareTo(param) >= 0) {
565                     break;
566                 }
567             }
568             System.arraycopy(params, i, params, i + 1, len - i);
569             params[i] = param;
570             len++;
571         }
572 
573         for (int i = 0 ; i < len ; i++) {
574             String param = params[i];
575             if (systemParam.get(param) == null) {
576                 out.println("<param name=" + param +
577                             " value=\"" + atts.get(param) + "\">");
578             }
579         }
580         out.println("</applet>");
581     }
582 
583     /**
584      * Make sure the atrributes are uptodate.
585      */
586     public void updateAtts() {
587         Dimension d = panel.size();
588         Insets in = panel.insets();
589         panel.atts.put("width",
590                        Integer.toString(d.width - (in.left + in.right)));
591         panel.atts.put("height",
592                        Integer.toString(d.height - (in.top + in.bottom)));
593     }
594 
595     /**
596      * Restart the applet.
597      */
598     void appletRestart() {
599         panel.sendEvent(AppletPanel.APPLET_STOP);
600         panel.sendEvent(AppletPanel.APPLET_DESTROY);
601         panel.sendEvent(AppletPanel.APPLET_INIT);
602         panel.sendEvent(AppletPanel.APPLET_START);
603     }
604 
605     /**
606      * Reload the applet.
607      */
608     void appletReload() {
609         panel.sendEvent(AppletPanel.APPLET_STOP);
610         panel.sendEvent(AppletPanel.APPLET_DESTROY);
611         panel.sendEvent(AppletPanel.APPLET_DISPOSE);
612 
613         /**
614          * Fixed #4501142: Classlaoder sharing policy doesn't
615          * take "archive" into account. This will be overridden
616          * by Java Plug-in.                     [stanleyh]
617          */
618         AppletPanel.flushClassLoader(panel.getClassLoaderCacheKey());
619 
620         /*
621          * Make sure we don't have two threads running through the event queue
622          * at the same time.
623          */
624         try {
625             panel.joinAppletThread();
626             panel.release();
627         } catch (InterruptedException e) {
628             return;   // abort the reload
629         }
630 
631         panel.createAppletThread();
632         panel.sendEvent(AppletPanel.APPLET_LOAD);
633         panel.sendEvent(AppletPanel.APPLET_INIT);
634         panel.sendEvent(AppletPanel.APPLET_START);
635     }
636 
637     /**
638      * Save the applet to a well known file (for now) as a serialized object
639      */
640     void appletSave() {
641         AccessController.doPrivileged(new PrivilegedAction() {
642 
643             public Object run() {
644                 // XXX: this privileged block should be made smaller
645                 // by initializing a private static variable with "user.dir"
646 
647                 // Applet needs to be stopped for serialization to succeed.
648                 // Since panel.sendEvent only queues the event, there is a
649                 // chance that the event will not be processed before
650                 // serialization begins.  However, by sending the event before
651                 // FileDialog is created, enough time is given such that this
652                 // situation is unlikely to ever occur.
653 
654                 panel.sendEvent(AppletPanel.APPLET_STOP);
655                 FileDialog fd = new FileDialog(AppletViewer.this,
656                                                amh.getMessage("appletsave.filedialogtitle"),
657                                                FileDialog.SAVE);
658                 // needed for a bug under Solaris...
659                 fd.setDirectory(System.getProperty("user.dir"));
660                 fd.setFile(defaultSaveFile);
661                 fd.show();
662                 String fname = fd.getFile();
663                 if (fname == null) {
664                     // Restart applet if Save is cancelled.
665                     panel.sendEvent(AppletPanel.APPLET_START);
666                     return null;                // cancelled
667                 }
668                 String dname = fd.getDirectory();
669                 File file = new File(dname, fname);
670 
671                 try {
672                     BufferedOutputStream s = new BufferedOutputStream(new FileOutputStream(file));
673                     ObjectOutputStream os = new ObjectOutputStream(s);
674                     showStatus(amh.getMessage("appletsave.err1",
675                                               panel.applet.toString(), file.toString()));
676                     os.writeObject(panel.applet);
677                 } catch (IOException ex) {
678                     System.err.println(amh.getMessage("appletsave.err2", ex));
679                 } finally {
680                     panel.sendEvent(AppletPanel.APPLET_START);
681                 }
682                 return null;
683             }
684         });
685     }
686 
687     /**
688      * Clone the viewer and the applet.
689      */
690     void appletClone() {
691         Point p = location();
692         updateAtts();
693         factory.createAppletViewer(p.x + XDELTA, p.y + YDELTA,
694                                    panel.documentURL, (Hashtable)panel.atts.clone());
695     }
696 
697     /**
698      * Show the applet tag.
699      */
700     void appletTag() {
701         ByteArrayOutputStream out = new ByteArrayOutputStream();
702         updateAtts();
703         printTag(new PrintStream(out), panel.atts);
704         showStatus(amh.getMessage("applettag"));
705 
706         Point p = location();
707         new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("applettag.textframe"), out.toString());
708     }
709 
710     /**
711      * Show the applet info.
712      */
713     void appletInfo() {
714         String str = panel.applet.getAppletInfo();
715         if (str == null) {
716             str = amh.getMessage("appletinfo.applet");
717         }
718         str += "\n\n";
719 
720         String atts[][] = panel.applet.getParameterInfo();
721         if (atts != null) {
722             for (int i = 0 ; i < atts.length ; i++) {
723                 str += atts[i][0] + " -- " + atts[i][1] + " -- " + atts[i][2] + "\n";
724             }
725         } else {
726             str += amh.getMessage("appletinfo.param");
727         }
728 
729         Point p = location();
730         new TextFrame(p.x + XDELTA, p.y + YDELTA, amh.getMessage("appletinfo.textframe"), str);
731 
732     }
733 
734     /**
735      * Show character encoding type
736      */
737     void appletCharacterEncoding() {
738         showStatus(amh.getMessage("appletencoding", encoding));
739     }
740 
741     /**
742      * Edit the applet.
743      */
744     void appletEdit() {
745     }
746 
747     /**
748      * Print the applet.
749      */
750     void appletPrint() {
751         PrinterJob pj = PrinterJob.getPrinterJob();
752 
753         if (pj != null) {
754             PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
755             if (pj.printDialog(aset)) {
756                 pj.setPrintable(this);
757                 try {
758                     pj.print(aset);
759                     statusMsgStream.println(amh.getMessage("appletprint.finish"));
760                 } catch (PrinterException e) {
761                    statusMsgStream.println(amh.getMessage("appletprint.fail"));
762                 }
763             } else {
764                 statusMsgStream.println(amh.getMessage("appletprint.cancel"));
765             }
766         } else {
767             statusMsgStream.println(amh.getMessage("appletprint.fail"));
768         }
769     }
770 
771     public int print(Graphics graphics, PageFormat pf, int pageIndex) {
772         if (pageIndex > 0) {
773             return Printable.NO_SUCH_PAGE;
774         } else {
775             Graphics2D g2d = (Graphics2D)graphics;
776             g2d.translate(pf.getImageableX(), pf.getImageableY());
777             panel.applet.printAll(graphics);
778             return Printable.PAGE_EXISTS;
779         }
780     }
781 
782     /**
783      * Properties.
784      */
785     static AppletProps props;
786     public static synchronized void networkProperties() {
787         if (props == null) {
788             props = new AppletProps();
789         }
790         props.addNotify();
791         props.setVisible(true);
792     }
793 
794     /**
795      * Start the applet.
796      */
797     void appletStart() {
798         panel.sendEvent(AppletPanel.APPLET_START);
799     }
800 
801     /**
802      * Stop the applet.
803      */
804     void appletStop() {
805         panel.sendEvent(AppletPanel.APPLET_STOP);
806     }
807 
808     /**
809      * Shutdown a viewer.
810      * Stop, Destroy, Dispose and Quit a viewer
811      */
812     private void appletShutdown(AppletPanel p) {
813         p.sendEvent(AppletPanel.APPLET_STOP);
814         p.sendEvent(AppletPanel.APPLET_DESTROY);
815         p.sendEvent(AppletPanel.APPLET_DISPOSE);
816         p.sendEvent(AppletPanel.APPLET_QUIT);
817     }
818 
819     /**
820      * Close this viewer.
821      * Stop, Destroy, Dispose and Quit an AppletView, then
822      * reclaim resources and exit the program if this is
823      * the last applet.
824      */
825     void appletClose() {
826 
827         // The caller thread is event dispatch thread, so
828         // spawn a new thread to avoid blocking the event queue
829         // when calling appletShutdown.
830         //
831         final AppletPanel p = panel;
832 
833         new Thread(new Runnable()
834         {
835             public void run()
836             {
837                 appletShutdown(p);
838                 appletPanels.removeElement(p);
839                 dispose();
840 
841                 if (countApplets() == 0) {
842                     appletSystemExit();
843                 }
844             }
845         }).start();
846     }
847 
848     /**
849      * Exit the program.
850      * Exit from the program (if not stand alone) - do no clean-up
851      */
852     private void appletSystemExit() {
853         if (factory.isStandalone())
854             System.exit(0);
855     }
856 
857     /**
858      * Quit all viewers.
859      * Shutdown all viewers properly then
860      * exit from the program (if not stand alone)
861      */
862     protected void appletQuit()
863     {
864         // The caller thread is event dispatch thread, so
865         // spawn a new thread to avoid blocking the event queue
866         // when calling appletShutdown.
867         //
868         new Thread(new Runnable()
869         {
870             public void run()
871             {
872                 for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) {
873                     AppletPanel p = (AppletPanel)e.nextElement();
874                     appletShutdown(p);
875                 }
876                 appletSystemExit();
877             }
878         }).start();
879     }
880 
881     /**
882      * Handle events.
883      */
884     public void processUserAction(ActionEvent evt) {
885 
886         String label = ((MenuItem)evt.getSource()).getLabel();
887 
888         if (amh.getMessage("menuitem.restart").equals(label)) {
889             appletRestart();
890             return;
891         }
892 
893         if (amh.getMessage("menuitem.reload").equals(label)) {
894             appletReload();
895             return;
896         }
897 
898         if (amh.getMessage("menuitem.clone").equals(label)) {
899             appletClone();
900             return;
901         }
902 
903         if (amh.getMessage("menuitem.stop").equals(label)) {
904             appletStop();
905             return;
906         }
907 
908         if (amh.getMessage("menuitem.save").equals(label)) {
909             appletSave();
910             return;
911         }
912 
913         if (amh.getMessage("menuitem.start").equals(label)) {
914             appletStart();
915             return;
916         }
917 
918         if (amh.getMessage("menuitem.tag").equals(label)) {
919             appletTag();
920             return;
921         }
922 
923         if (amh.getMessage("menuitem.info").equals(label)) {
924             appletInfo();
925             return;
926         }
927 
928         if (amh.getMessage("menuitem.encoding").equals(label)) {
929             appletCharacterEncoding();
930             return;
931         }
932 
933         if (amh.getMessage("menuitem.edit").equals(label)) {
934             appletEdit();
935             return;
936         }
937 
938         if (amh.getMessage("menuitem.print").equals(label)) {
939             appletPrint();
940             return;
941         }
942 
943         if (amh.getMessage("menuitem.props").equals(label)) {
944             networkProperties();
945             return;
946         }
947 
948         if (amh.getMessage("menuitem.close").equals(label)) {
949             appletClose();
950             return;
951         }
952 
953         if (factory.isStandalone() && amh.getMessage("menuitem.quit").equals(label)) {
954             appletQuit();
955             return;
956         }
957         //statusMsgStream.println("evt = " + evt);
958     }
959 
960     /**
961      * How many applets are running?
962      */
963 
964     public static int countApplets() {
965         return appletPanels.size();
966     }
967 
968 
969     /**
970      * The current character.
971      */
972     static int c;
973 
974     /**
975      * Scan spaces.
976      */
977     public static void skipSpace(Reader in) throws IOException {
978         while ((c >= 0) &&
979                ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'))) {
980             c = in.read();
981         }
982     }
983 
984     /**
985      * Scan identifier
986      */
987     public static String scanIdentifier(Reader in) throws IOException {
988         StringBuffer buf = new StringBuffer();
989         while (true) {
990             if (((c >= 'a') && (c <= 'z')) ||
991                 ((c >= 'A') && (c <= 'Z')) ||
992                 ((c >= '0') && (c <= '9')) || (c == '_')) {
993                 buf.append((char)c);
994                 c = in.read();
995             } else {
996                 return buf.toString();
997             }
998         }
999     }
1000 
1001     /**
1002      * Scan tag
1003      */
1004     public static Hashtable scanTag(Reader in) throws IOException {
1005         Hashtable atts = new Hashtable();
1006         skipSpace(in);
1007         while (c >= 0 && c != '>') {
1008             String att = scanIdentifier(in);
1009             String val = "";
1010             skipSpace(in);
1011             if (c == '=') {
1012                 int quote = -1;
1013                 c = in.read();
1014                 skipSpace(in);
1015                 if ((c == '\'') || (c == '\"')) {
1016                     quote = c;
1017                     c = in.read();
1018                 }
1019                 StringBuffer buf = new StringBuffer();
1020                 while ((c > 0) &&
1021                        (((quote < 0) && (c != ' ') && (c != '\t') &&
1022                          (c != '\n') && (c != '\r') && (c != '>'))
1023                         || ((quote >= 0) && (c != quote)))) {
1024                     buf.append((char)c);
1025                     c = in.read();
1026                 }
1027                 if (c == quote) {
1028                     c = in.read();
1029                 }
1030                 skipSpace(in);
1031                 val = buf.toString();
1032             }
1033             //statusMsgStream.println("PUT " + att + " = '" + val + "'");
1034             if (! val.equals("")) {
1035                 atts.put(att.toLowerCase(java.util.Locale.ENGLISH), val);
1036             }
1037             while (true) {
1038                 if ((c == '>') || (c < 0) ||
1039                     ((c >= 'a') && (c <= 'z')) ||
1040                     ((c >= 'A') && (c <= 'Z')) ||
1041                     ((c >= '0') && (c <= '9')) || (c == '_'))
1042                     break;
1043                 c = in.read();
1044             }
1045             //skipSpace(in);
1046         }
1047         return atts;
1048     }
1049 
1050     /* values used for placement of AppletViewer's frames */
1051     private static int x = 0;
1052     private static int y = 0;
1053     private static final int XDELTA = 30;
1054     private static final int YDELTA = XDELTA;
1055 
1056     static String encoding = null;
1057 
1058     static private Reader makeReader(InputStream is) {
1059         if (encoding != null) {
1060             try {
1061                 return new BufferedReader(new InputStreamReader(is, encoding));
1062             } catch (IOException x) { }
1063         }
1064         InputStreamReader r = new InputStreamReader(is);
1065         encoding = r.getEncoding();
1066         return new BufferedReader(r);
1067     }
1068 
1069     /**
1070      * Scan an html file for <applet> tags
1071      */
1072     public static void parse(URL url, String enc) throws IOException {
1073         encoding = enc;
1074         parse(url, System.out, new StdAppletViewerFactory());
1075     }
1076 
1077     public static void parse(URL url) throws IOException {
1078         parse(url, System.out, new StdAppletViewerFactory());
1079     }
1080 
1081     public static void parse(URL url, PrintStream statusMsgStream,
1082                              AppletViewerFactory factory) throws IOException {
1083         // <OBJECT> <EMBED> tag flags
1084         boolean isAppletTag = false;
1085         boolean isObjectTag = false;
1086         boolean isEmbedTag = false;
1087 
1088         // warning messages
1089         String requiresNameWarning = amh.getMessage("parse.warning.requiresname");
1090         String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside");
1091         String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode");
1092         String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight");
1093         String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth");
1094         String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode");
1095         String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight");
1096         String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth");
1097         String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode");
1098         String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight");
1099         String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth");
1100         String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported");
1101 
1102         java.net.URLConnection conn = url.openConnection();
1103         Reader in = makeReader(conn.getInputStream());
1104         /* The original URL may have been redirected - this
1105          * sets it to whatever URL/codebase we ended up getting
1106          */
1107         url = conn.getURL();
1108 
1109         int ydisp = 1;
1110         Hashtable atts = null;
1111 
1112         while(true) {
1113             c = in.read();
1114             if (c == -1)
1115                 break;
1116 
1117             if (c == '<') {
1118                 c = in.read();
1119                 if (c == '/') {
1120                     c = in.read();
1121                     String nm = scanIdentifier(in);
1122                     if (nm.equalsIgnoreCase("applet") ||
1123                         nm.equalsIgnoreCase("object") ||
1124                         nm.equalsIgnoreCase("embed")) {
1125 
1126                         // We can't test for a code tag until </OBJECT>
1127                         // because it is a parameter, not an attribute.
1128                         if(isObjectTag) {
1129                             if (atts.get("code") == null && atts.get("object") == null) {
1130                                 statusMsgStream.println(objectRequiresCodeWarning);
1131                                 atts = null;
1132                             }
1133                         }
1134 
1135                         if (atts != null) {
1136                             // XXX 5/18 In general this code just simply
1137                             // shouldn't be part of parsing.  It's presence
1138                             // causes things to be a little too much of a
1139                             // hack.
1140                             factory.createAppletViewer(x, y, url, atts);
1141                             x += XDELTA;
1142                             y += YDELTA;
1143                             // make sure we don't go too far!
1144                             Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
1145                             if ((x > d.width - 300) || (y > d.height - 300)) {
1146                                 x = 0;
1147                                 y = 2 * ydisp * YDELTA;
1148                                 ydisp++;
1149                             }
1150                         }
1151                         atts = null;
1152                         isAppletTag = false;
1153                         isObjectTag = false;
1154                         isEmbedTag = false;
1155                     }
1156                 }
1157                 else {
1158                     String nm = scanIdentifier(in);
1159                     if (nm.equalsIgnoreCase("param")) {
1160                         Hashtable t = scanTag(in);
1161                         String att = (String)t.get("name");
1162                         if (att == null) {
1163                             statusMsgStream.println(requiresNameWarning);
1164                         } else {
1165                             String val = (String)t.get("value");
1166                             if (val == null) {
1167                                 statusMsgStream.println(requiresNameWarning);
1168                             } else if (atts != null) {
1169                                 atts.put(att.toLowerCase(), val);
1170                             } else {
1171                                 statusMsgStream.println(paramOutsideWarning);
1172                             }
1173                         }
1174                     }
1175                     else if (nm.equalsIgnoreCase("applet")) {
1176                         isAppletTag = true;
1177                         atts = scanTag(in);
1178                         if (atts.get("code") == null && atts.get("object") == null) {
1179                             statusMsgStream.println(appletRequiresCodeWarning);
1180                             atts = null;
1181                         } else if (atts.get("width") == null) {
1182                             statusMsgStream.println(appletRequiresWidthWarning);
1183                             atts = null;
1184                         } else if (atts.get("height") == null) {
1185                             statusMsgStream.println(appletRequiresHeightWarning);
1186                             atts = null;
1187                         }
1188                     }
1189                     else if (nm.equalsIgnoreCase("object")) {
1190                         isObjectTag = true;
1191                         atts = scanTag(in);
1192                         // The <OBJECT> attribute codebase isn't what
1193                         // we want. If its defined, remove it.
1194                         if(atts.get("codebase") != null) {
1195                             atts.remove("codebase");
1196                         }
1197 
1198                         if (atts.get("width") == null) {
1199                             statusMsgStream.println(objectRequiresWidthWarning);
1200                             atts = null;
1201                         } else if (atts.get("height") == null) {
1202                             statusMsgStream.println(objectRequiresHeightWarning);
1203                             atts = null;
1204                         }
1205                     }
1206                     else if (nm.equalsIgnoreCase("embed")) {
1207                         isEmbedTag = true;
1208                         atts = scanTag(in);
1209 
1210                         if (atts.get("code") == null && atts.get("object") == null) {
1211                             statusMsgStream.println(embedRequiresCodeWarning);
1212                             atts = null;
1213                         } else if (atts.get("width") == null) {
1214                             statusMsgStream.println(embedRequiresWidthWarning);
1215                             atts = null;
1216                         } else if (atts.get("height") == null) {
1217                             statusMsgStream.println(embedRequiresHeightWarning);
1218                             atts = null;
1219                         }
1220                     }
1221                     else if (nm.equalsIgnoreCase("app")) {
1222                         statusMsgStream.println(appNotLongerSupportedWarning);
1223                         Hashtable atts2 = scanTag(in);
1224                         nm = (String)atts2.get("class");
1225                         if (nm != null) {
1226                             atts2.remove("class");
1227                             atts2.put("code", nm + ".class");
1228                         }
1229                         nm = (String)atts2.get("src");
1230                         if (nm != null) {
1231                             atts2.remove("src");
1232                             atts2.put("codebase", nm);
1233                         }
1234                         if (atts2.get("width") == null) {
1235                             atts2.put("width", "100");
1236                         }
1237                         if (atts2.get("height") == null) {
1238                             atts2.put("height", "100");
1239                         }
1240                         printTag(statusMsgStream, atts2);
1241                         statusMsgStream.println();
1242                     }
1243                 }
1244             }
1245         }
1246         in.close();
1247     }
1248 
1249     /**
1250      * Old main entry point.
1251      *
1252      * @deprecated
1253      */
1254     @Deprecated
1255     public static void main(String argv[]) {
1256         // re-route everything to the new main entry point
1257         Main.main(argv);
1258     }
1259 
1260     private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer");
1261 
1262     private static void checkConnect(URL url)
1263     {
1264         SecurityManager security = System.getSecurityManager();
1265         if (security != null) {
1266             try {
1267                 java.security.Permission perm =
1268                     url.openConnection().getPermission();
1269                 if (perm != null)
1270                     security.checkPermission(perm);
1271                 else
1272                     security.checkConnect(url.getHost(), url.getPort());
1273             } catch (java.io.IOException ioe) {
1274                     security.checkConnect(url.getHost(), url.getPort());
1275             }
1276         }
1277     }
1278 }